package com.graduate.emos.wx.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateRange;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.graduate.emos.wx.common.util.SystemConstants;
import com.graduate.emos.wx.db.dao.*;
import com.graduate.emos.wx.db.pojo.TbCheckin;
import com.graduate.emos.wx.db.pojo.TbFaceModel;
import com.graduate.emos.wx.exception.EmosException;
import com.graduate.emos.wx.service.CheckinService;
import com.graduate.emos.wx.task.EmailTask;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.stereotype.Service;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;

@Service
@Scope("prototype")
@Slf4j
public class CheckinServiceImpl implements CheckinService {

    /*注入对象*/
    @Autowired
    private SystemConstants constants;

    @Autowired
    private TbHolidaysDao tbHolidaysDao;

    @Autowired
    private TbWorkdayDao tbWorkdayDao;

    @Autowired
    private TbCheckinDao tbCheckinDao;

    @Autowired
    private TbCityDao tbCityDao;

    @Autowired
    private TbFaceModelDao faceModelDao;

    @Autowired
    private TbUserDao tbUserDao;

    @Value("${emos.face.createFaceModelUrl}")
    private String createFaceModelUrl;

    @Value("${emos.face.checkinUrl}")
    private String checkinUrl;

    @Value("${emos.email.boss}")
    private String bossEmail;

    @Value("${emos.email.king}")
    private String kingEmail;

    @Value("${emos.code}")
    private String code;

    @Autowired
    private EmailTask emailTask;

    @Override
    public String validCanCheckin(int userId, String date) {
        /*查询当前日期是否在特殊节假日表中*/
        boolean isHoliday = tbHolidaysDao.searchTodayIsHoliday() != null;
        /*查询当前日期是否在特殊工作日表中*/
        boolean isWorkday = tbWorkdayDao.searchTodayIsWorkday() != null;
        //默认现在的日期是工作日
        String nowType = constants.CODE_WORKDAY;

        if (DateUtil.date().isWeekend())
            nowType = constants.CODE_HOLIDAY;

        if (isHoliday)
            nowType = constants.CODE_HOLIDAY;
        else if (isWorkday)
            nowType = constants.CODE_WORKDAY;

        if (constants.CODE_HOLIDAY.equals(nowType)) {
            /*当前时间是节假日,不需要签到*/
            return constants.CODE_CAN_NOT_CHECKIN_03;
        } else {
            /*当前时间为工作日,需要签到
             * 判断当前时间是否在合法的
             * 签到时间内*/
            DateTime curTime = DateUtil.date();
            String start = DateUtil.today() + " " + constants.attendanceStartTime;
            String end = DateUtil.today() + " " + constants.attendanceEndTime;
            DateTime attendanceStart = DateUtil.parse(start);
            DateTime attendanceEnd = DateUtil.parse(end);
            if (curTime.before(attendanceStart)) {
                /*如果当前时间比考勤时间早,也不能签到*/
                return constants.CODE_CAN_NOT_CHECKIN_01;
            } else if (curTime.after(attendanceEnd)) {
                /*如果当前时间已经超过了考勤最晚时间,也不能签到*/
                return constants.CODE_CAN_NOT_CHECKIN_02;
            } else {
                /*在合法的签到时间内，再判断当前用户是否已经签过到*/
                HashMap<String, Object> map = new HashMap<>();
                map.put("userId", userId);
                map.put("date", date);
                map.put("start", attendanceStart);
                map.put("end", attendanceEnd);
                return tbCheckinDao.haveCheckin(map) == null ? constants.CODE_CAN_CHECKIN : constants.CODE_CAN_NOT_CHECKIN_04;
            }
        }
    }

    @Override
    public void checkin(HashMap inParam) {
        /*校验签到时间*/
        /*获取当前时间*/
        Date now = DateUtil.date();
        /*获取当天的上班时间*/
        Date attendanceTime = DateUtil.parse(DateUtil.today() + " " + constants.attendanceTime);
        /*获取当天的下班时间*/
        Date attendanceEndTime = DateUtil.parse(DateUtil.today() + " " + constants.attendanceEndTime);
        /*记录签到状态[2迟到、1正常]*/
        int attendanceStatus = 1;
        attendanceStatus = now.compareTo(attendanceTime) <= 0 ?
                1 : now.compareTo(attendanceTime) > 0 && now.compareTo(attendanceEndTime) < 0 ?
                2 : attendanceStatus;

        /*从参数集合中获取用户id后使用该id去查询人脸模型数据*/
        int userId = (Integer) inParam.get("userId");
        String faceModel = faceModelDao.searchFaceModel(userId);
        if (Objects.isNull(faceModel)) {
            /*没有找到人脸模型*/
            throw new EmosException(constants.FACE_MODEL_IS_NOT_EXIST_MSG);
        } else {
            /*找到人脸模型，启动人脸识别流程*/
            /*获取照片路径*/
            String path = (String) inParam.get("path");
            /*发起Http请求*/
            HttpRequest request = HttpUtil.createPost(checkinUrl);
            /*准备上传的数据[照片文件的路径，文件名，人脸模型数据]*/
            request.form("photo", FileUtil.file(path), "targetModel", faceModel);
            request.form("code",code);
            /*发起请求，获取响应*/
            HttpResponse response = request.execute();
            if (response.getStatus() != 200) {
                /*如果响应码不是200,则过程中出现了异常*/
                log.error(constants.FACE_RECOGNITION_SERVER_EXCEPTION_MSG);
                throw new EmosException(constants.FACE_RECOGNITION_SERVER_EXCEPTION_MSG);
            }

            /*未发生异常可顺利获得响应内容*/
            String body = response.body();
            /*根据响应的内容判断签到情况*/
            if (constants.FACE_RECOGNITION_FAILED_MSG.equals(body) || constants.THERE_ARE_MULTIPLE_FACES_MSG.equals(body) || constants.EXECUTE_ERROR.equals(body))
                throw new EmosException(body);
            else if (constants.FACE_RECOGNITION_RESULT_FALSE.equals(body))
                throw new EmosException(constants.FACE_RECOGNITION_RESULT_NOT_SELF_MSG);
            else if (constants.FACE_RECOGNITION_RESULT_TRUE.equals(body)) {
                /*查询疫情风险等级*/
                /*记录当前地区疫情风险等级1.低风险 2.中风险 3.高风险*/
                int risk = 1;
                /*获取城市和区县*/
                String city = (String) inParam.get("city");
                String district = (String) inParam.get("district");
                String address = (String) inParam.get("address");
                if (StringUtils.isNotBlank(city) && StringUtils.isNotBlank(district)) {
                    /*查询城市代码*/
                    String cityCode = tbCityDao.searchCode(city);
                    /*查询疫情风险等级*/
                    try {
                        String url = "http://m." + cityCode + ".bendibao.com/news/yqdengji/?qu=" + district;
                        /*通过Jsoup.connect().get()获取到url对应的html*/
                        Document document = Jsoup.connect(url).get();
                        /*解析HTML获取到疫情等级*/
                        Elements elements = document.getElementsByClass("list-content");
                        if (elements.size() > 0) {
                            String grade = elements.first().children().last().text();

                            /*测试高风险地区邮件发送情况*/
                            grade = constants.HIGH_RISK_OF_OUTBREAK;

                            risk = constants.HIGH_RISK_OF_OUTBREAK.equals(grade) ?
                                    3 : constants.MID_RISK_OF_OUTBREAK.equals(grade) ?
                                    2 : risk;
                        }
                        if (risk == 3) {
                            /*发送告警邮件*/
                            /*查询用户名称和所在部门名称*/
                            HashMap<String, String> userInfo = tbUserDao.searchNameAndDept(userId);
                            String name = userInfo.get("name");
                            String deptName = userInfo.get("dept_name");
                            deptName = deptName != null ? deptName : "";
                            SimpleMailMessage message = new SimpleMailMessage();
                            /*发送给谁*/
                            message.setTo(bossEmail,kingEmail);
                            /*邮件标题*/
                            message.setSubject("员工" + name + constants.HIGH_RISK_OF_OUTBREAK_WARNING_SUBJECT );
                            /*正文*/
                            message.setText(deptName + "员工" + name + ","
                                    + DateUtil.format(new Date(),"yyyy年MM月dd日")
                                    + "处于" + address + "," + constants.HIGH_RISK_OF_OUTBREAK_WARNING_TEXT);
                            emailTask.sendAsync(message);
                        }
                    } catch (Exception e) {
                        log.error(constants.ACCESS_OUTBREAK_GRADE_FAILED);
                        throw new EmosException(constants.ACCESS_OUTBREAK_GRADE_FAILED);
                    }
                }
                /*保存签到记录*/
                String country = (String) inParam.get("country");
                String province = (String) inParam.get("province");
                TbCheckin entity = new TbCheckin();
                entity.setUserId(userId);
                entity.setAddress(address);
                entity.setCountry(country);
                entity.setProvince(province);
                entity.setCity(city);
                entity.setDistrict(district);
                entity.setStatus((byte) attendanceStatus);
                entity.setDate(DateUtil.today());
                entity.setCreateTime(now);
                entity.setRisk(risk);
                tbCheckinDao.addCheckin(entity);
            }
        }
    }

    @Override
    public void createFaceModel(int userId, String path) {
        /*创建Http请求*/
        HttpRequest request = HttpUtil.createPost(createFaceModelUrl);
        /*准备请求携带的照片*/
        request.form("photo",FileUtil.file(path));
        request.form("code",code);
        /*发起请求接收响应*/
        HttpResponse response = request.execute();
        if (response.getStatus() != 200) {
            /*如果响应码不是200,则过程中出现了异常*/
            log.error(constants.FACE_RECOGNITION_SERVER_EXCEPTION_MSG);
            throw new EmosException(constants.FACE_RECOGNITION_SERVER_EXCEPTION_MSG);
        }
        String body = response.body();
        if (constants.FACE_RECOGNITION_FAILED_MSG.equals(body) || constants.THERE_ARE_MULTIPLE_FACES_MSG.equals(body) || constants.EXECUTE_ERROR.equals(body))
            throw new EmosException(body);
        else{
            /*存储人脸模型*/
            TbFaceModel entity = new TbFaceModel();
            entity.setUserId(userId);
            entity.setFaceModel(body);
            faceModelDao.addFaceModel(entity);
        }
    }

    @Override
    public HashMap searchTodayCheckin(int userId) {
        return tbCheckinDao.searchTodayCheckin(userId);
    }

    @Override
    public long searchCheckinDays(int userId) {
        return tbCheckinDao.searchCheckinDays(userId);
    }

    @Override
    public ArrayList<HashMap> searchWeekCheckin(HashMap inParam) {
        /*存储签到数据*/
        ArrayList<HashMap> list = new ArrayList<HashMap>();
        /*获取这周的原始签到数据*/
        ArrayList<HashMap> checkinList = tbCheckinDao.searchWeekCheckin(inParam);
        /*获取这周的节假日数据*/
        ArrayList<String> holidays = tbHolidaysDao.searchHolidaysInRange(inParam);
        /*获取这周的工作日数据*/
        ArrayList<String> workdays = tbWorkdayDao.searchWorkdayInRange(inParam);
        /*获取这周的开始日期和结束日期*/
        DateTime startDate = DateUtil.parseDate(inParam.get("startDate").toString());
        DateTime endDate = DateUtil.parseDate(inParam.get("endDate").toString());
        /*构造日期范围对象*/
        DateRange dateRange = DateUtil.range(startDate,endDate, DateField.DAY_OF_MONTH);
        dateRange.forEach(one->{
            /*判断当天是工作日还是休息日,是工作日需要确定考勤状态status*/
            String date = one.toString("yyyy-MM-dd");
            String dateType = constants.CODE_WORKDAY;
            if (one.isWeekend()){
                dateType = constants.CODE_HOLIDAY;
            }
            if (!Objects.isNull(holidays) && holidays.contains(date)){
                /*如果当天是特殊的节假日*/
                dateType = constants.CODE_HOLIDAY;
            }else if(!Objects.isNull(workdays) && workdays.contains(date)){
                /*如果当天是特殊的工作日*/
                dateType = constants.CODE_WORKDAY;
            }
            /*默认考勤状态为空字符串*/
            String status = "";
            if (dateType.equals(constants.CODE_WORKDAY) && DateUtil.compare(one,DateUtil.date()) <= 0){
                /*已经过了的日期以及日期是工作日需要考虑变更考勤状态(默认状态为[缺勤])*/
                status = constants.CODE_ABSENCE;
                /*标识是否在签到记录中查找到结果*/
                boolean flag = false;
                /*从签到数据中查找有没有当前日期的签到记录*/
                for (HashMap<String, String> checkinRecord : checkinList) {
                    if (checkinRecord.containsValue(date)){
                        /*如果找到了当前考勤记录,重新赋值考勤状态*/
                        status = checkinRecord.get("status");
                        flag = true;
                        break;
                    }
                }
                /*如果没有找到签到记录，根据判断当前时间是否已经过了今天的签到时间
                如果过了签到时间则状态为缺勤,如果还没有过签到时间状态要变更为空字符串*/
                DateTime endTime = DateUtil.parse(DateUtil.today() + " " + constants.getAttendanceEndTime());
                String today = DateUtil.today();
                if (date.equals(today) && DateUtil.date().isBefore(endTime) && !flag){
                    /*当前日期没有查询到签到记录且还没有错过签到时间*/
                    status = "";
                }
            }
            /*封装签到信息数据*/
            HashMap map = new HashMap();
            map.put("date", date);
            map.put("status", status);
            map.put("type", dateType);
            map.put("day", one.dayOfWeekEnum().toChinese("周"));
            list.add(map);
        });
        return list;
    }

    @Override
    public ArrayList<HashMap> searchMonthCheckin(HashMap inParam) {
        /*使用searchWeekCheckin()方法，传入不同的起止日期
        可以实现查询一个月范围内的签到情况*/
        return searchWeekCheckin(inParam);
    }
}